package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.RequestParam;
import cn.itcast.hotel.pojo.pageResult;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.queries.function.FunctionScoreQuery;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private RestHighLevelClient client;
    @Override
    public pageResult searchByParam(RequestParam param) throws Exception{
        SearchRequest searchRequest = new SearchRequest("hotel");

        basicQuerBuild(searchRequest,param);

        //分词查询


        //地理位置排序
        //searchRequest.source().sort("price", SortOrder.DESC);

        if(StringUtils.isNotBlank(param.getLocation())){
            searchRequest.source().sort(SortBuilders.geoDistanceSort("location",new GeoPoint(param.getLocation())).
                    order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));//DistanceUnit.
        }

        //分页
        Integer from = (param.getPage() - 1) * param.getSize();
        searchRequest.source().from(from);
        searchRequest.source().size(param.getSize());

        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.preTags("<em style= 'color:red'>");
        highlightBuilder.postTags("</em>");
        highlightBuilder.requireFieldMatch(false);
        searchRequest.source().highlighter(highlightBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        //handlerResponse(searchResponse);

        return handlerResponse(searchResponse);
    }

    @Override
    public Map<String, List<String>> filters(RequestParam param) throws Exception{
        Map<String,List<String>> resultMap = new HashMap<>();
        SearchRequest searchRequest = new SearchRequest("hotel");
        basicQuerBuild(searchRequest,param);
        searchRequest.source().size(0);
        searchRequest.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(100));
        searchRequest.source().aggregation(AggregationBuilders.terms("starNameAgg").field("starName").size(100));
        searchRequest.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(100));
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        Aggregations aggregations = searchResponse.getAggregations();
        //处理brandAgg
        Terms brandAggTerms = aggregations.get("brandAgg");
        List<? extends Terms.Bucket> buckets = brandAggTerms.getBuckets();
        List<String> brandList = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            brandList.add(bucket.getKeyAsString());
        }
        resultMap.put("brand",brandList);



        //处理starAgg
        Terms starNameAggTerms = aggregations.get("starNameAgg");
        List<? extends Terms.Bucket> starNameBuckets = starNameAggTerms.getBuckets();

        List<String> starNameList = new ArrayList<>();
        for (Terms.Bucket bucket : starNameBuckets) {
            starNameList.add(bucket.getKeyAsString());
        }
        resultMap.put("starName",starNameList);



        //处理cityAgg
        Terms cityAggTerms = aggregations.get("cityAgg");
        List<? extends Terms.Bucket> cityAggBuckets = cityAggTerms.getBuckets();

        List<String> cityList = new ArrayList<>();
        for (Terms.Bucket bucket : cityAggBuckets) {
            cityList.add(bucket.getKeyAsString());
        }
        resultMap.put("city",cityList);
        return resultMap;
    }

    @Override
    public List<String> getSuggestions(String prefix) {
        try {
            // 1.准备Request
            SearchRequest request = new SearchRequest("hotel");
            // 2.准备DSL
            request.source().suggest(new SuggestBuilder().addSuggestion(
                    "suggestions",
                    SuggestBuilders.completionSuggestion("suggestion")
                            .prefix(prefix)
                            .skipDuplicates(true)
                            .size(10)
            ));
            // 3.发起请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 4.解析结果
            Suggest suggest = response.getSuggest();
            // 4.1.根据补全查询名称，获取补全结果
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
            // 4.2.获取options
            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
            // 4.3.遍历
            List<String> list = new ArrayList<>(options.size());
            for (CompletionSuggestion.Entry.Option option : options) {
                String text = option.getText().toString();
                list.add(text);
            }
            return list;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }



    private void basicQuerBuild(SearchRequest searchRequest, RequestParam param) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        //如果关键字为空
        if (StringUtils.isBlank(param.getKey())){
            //searchRequest.source().query(QueryBuilders.matchAllQuery());
            boolQuery.must(QueryBuilders.matchAllQuery());
        }else{
            //searchRequest.source().query(QueryBuilders.matchQuery("all",param.getKey()));
            boolQuery.must(QueryBuilders.matchQuery("all",param.getKey()));
        }
        if(StringUtils.isNotBlank(param.getBrand())){
            boolQuery.filter(QueryBuilders.termQuery("brand",param.getBrand()));
        }
        if(StringUtils.isNotBlank(param.getCity())){
            boolQuery.filter(QueryBuilders.termQuery("city",param.getCity()));
        }
        if(StringUtils.isNotBlank(param.getStarName())){
            boolQuery.filter(QueryBuilders.termQuery("starName",param.getStarName()));
        }
        if(param.getMinPrice() != null && param.getMinPrice() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(param.getMinPrice()).lte(param.getMaxPrice()));
        }
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery
                (boolQuery,new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery
                                ("isAD",true), ScoreFunctionBuilders.weightFactorFunction(10))
                });
        //FunctionScoreQueryBuilder functionScoreQuery =
        //        QueryBuilders.functionScoreQuery(
        //                // 原始查询，相关性算分的查询
        //                boolQuery,
        //                // function score的数组
        //                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
        //                        // 其中的一个function score 元素
        //                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
        //                                // 过滤条件
        //                                QueryBuilders.termQuery("isAD", true),
        //                                // 算分函数
        //                                ScoreFunctionBuilders.weightFactorFunction(10)
        //                        )
        //                });
        searchRequest.source().query(functionScoreQueryBuilder);
    }

    private pageResult handlerResponse(SearchResponse searchResponse){
        SearchHits responseHits = searchResponse.getHits();
        long total = responseHits.getTotalHits().value;
        System.out.println("查询出复合条件的总数" + total);
        SearchHit[] hits = responseHits.getHits();
        List<HotelDoc> list = new ArrayList<>();
        for (SearchHit hit : hits) {
            String sourceAsString = hit.getSourceAsString();
            HotelDoc hotelDoc = JSON.parseObject(sourceAsString,HotelDoc.class);

            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            HighlightField highlightField = highlightFields.get("name");
            if(highlightField != null){
                Text[] fragments = highlightField.getFragments();
                if(fragments != null && fragments.length > 0){
                    String nameHighLight = fragments[0].toString();
                    hotelDoc.setName(nameHighLight);
                }
            }

            Object[] sortValues = hit.getSortValues();
            if(sortValues != null && sortValues.length > 0){
                hotelDoc.setDistance(sortValues[0]);
            }
            //System.out.println(hotelDoc);
            list.add(hotelDoc);
        }
        return new pageResult(list,total);
    }
    @Override
    public void saveOrUpdataetES(String hotelString) {
        Hotel hotel = JSON.parseObject(hotelString,Hotel.class);
        HotelDoc hotelDoc = new HotelDoc(hotel);
        IndexRequest request = new IndexRequest("hotel");
        request.id(hotelDoc.getId().toString());
        String jsonString = JSON.toJSONString(hotelDoc);
        request.source(jsonString, XContentType.JSON);
        try {
            IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteES(String idString) {
        DeleteRequest request = new DeleteRequest("hotel", idString);
        try {
            DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
